package cmpe273.proj1.model;

import java.util.Map.Entry;
import java.util.regex.Pattern;

import org.json.JSONException;
import org.json.JSONObject;

import cmpe273.proj1.utilities.Utilities;

public class Person {
	//public constants
	public final static int MAX_LENGTH_LASTNAME = 40;
	public final static int MAX_LENGTH_FIRSTNAME = 40;
	public final static int MAX_LENGTH_ADDRESS = 40;
	public final static int MAX_LENGTH_CITY = 40;
	public final static int MAX_LENGTH_STATE = 2;
	public final static int MAX_LENGTH_ZIPCODE = 10;
	
	//private constants
	private final static String VALID_ID_REGEX = "^[0-9]{3}-[0-9]{2}-[0-9]{4}$";
	private final static String VALID_ZIP_REGEX = "^[0-9]{5}(-[0-9]{4})?$";

	//private members
	private String lastName;
	private String firstName;
	private String address;
	private String city;
	private String state;
	private String zipCode;

	//constructors
	public Person() {
		this.lastName = "";
		this.firstName = "";
		this.address = "";
		this.city = "";
		this.state = "";
		this.zipCode = "";
	}
	
	public Person(String lastName, String firstName, String address, String city, String state, String zipCode) {
		this.lastName = lastName == null ? "" : lastName;
		this.firstName = firstName == null ? "" : firstName;
		this.address = address == null ? "" : address;
		this.city = city == null ? "" : city;
		this.setState(state == null ? "" : state);
		this.zipCode = zipCode == null ? "" : zipCode;
	}

	public Person(Person p) {
		this.lastName = p.getLastName();
		this.firstName = p.getFirstName();
		this.address = p.getAddress();
		this.city = p.getCity();
		this.state = p.getState();
		this.zipCode = p.getZipCode();
	}

	//accessors
	public String getLastName() {
		return lastName;
	}
	
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public String getFirstName() {
		return firstName;
	}
	
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	
	public String getAddress() {
		return address;
	}
	
	public void setAddress(String address) {
		this.address = address;
	}
	
	public String getCity() {
		return city;
	}
	
	public void setCity(String city) {
		this.city = city;
	}
	
	public String getState() {
		return state;
	}
	
	public void setState(String state) {
		this.state = state.length() > 2 ? Person.getStateAbbreviation(state) : state;
	}
	
	public String getZipCode() {
		return zipCode;
	}
	
	public void setZipCode(String zipCode) {
		this.zipCode = zipCode;
	}

	//json utility
	public boolean loadPerson(JSONObject jsonObj) {
		try {
			this.lastName = jsonObj.getString("lastName");
			this.firstName = jsonObj.getString("firstName");
			this.address = jsonObj.getString("address");
			this.city = jsonObj.getString("city");
			this.state = jsonObj.getString("state");
			this.zipCode = jsonObj.getString("zipCode");
		} catch (JSONException e) {
			System.out.println("Invalid Person jsonObj: " + jsonObj.toString());
			return false;
		}
		return true;
	}

	public boolean loadPerson(String jsonString) {
		try {
			JSONObject obj = new JSONObject(jsonString);

			return loadPerson(obj);
		} catch (JSONException e) {
			System.out.println("Invalid Person jsonString: " + jsonString);
			return false;
		}
	}

	public String toPartialJsonString() {
		String ret  = "\"lastName\":\"" + lastName + "\", \"firstName\":\"" + firstName + "\", \"address\":\"" + address + "\", \"city\":\"" + city + "\", \"state\":\"" + state + "\", \"zipCode\":\"" + zipCode + "\"";
		return ret;
	}
	
	//validation utilities
	public String validate() {
		StringBuilder sb = new StringBuilder();
		if(lastName.length() > MAX_LENGTH_LASTNAME)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "Lastname", MAX_LENGTH_LASTNAME));
		if(firstName.length() > MAX_LENGTH_FIRSTNAME)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "Firstname", MAX_LENGTH_FIRSTNAME));
		if(address.length() > MAX_LENGTH_ADDRESS)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "Address", MAX_LENGTH_ADDRESS));
		if(city.length() > MAX_LENGTH_CITY)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "City", MAX_LENGTH_CITY));
		if(state.length() > MAX_LENGTH_STATE)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "State", MAX_LENGTH_STATE));
		if(zipCode.length() > MAX_LENGTH_ZIPCODE)
			sb.append(String.format(Utilities.STRING_LENGTH_ERROR, "Zipcode", MAX_LENGTH_ZIPCODE));
		
		if(!state.isEmpty() && !Utilities.StateAbbreviationsAndNames.containsKey(state))
			sb.append(String.format(Utilities.STATE_FORMAT_ERROR));
		if(!validZipcode(zipCode))
			sb.append(String.format(Utilities.ZIPCODE_FORMAT_ERROR));
		
		return sb.toString();
	}
	
	public static boolean validId(String id) {
		return Pattern.matches(VALID_ID_REGEX, id);
	}
	
	private static boolean validZipcode(String zipcode) {
		return zipcode.isEmpty() || Pattern.matches(VALID_ZIP_REGEX, zipcode);
	}
	
	//general utility
	public static String getStateAbbreviation(String stateName) {
		if(stateName.length() < 3)
			return stateName;
		if(Utilities.StateAbbreviationsAndNames.containsValue(stateName)) {
			for(Entry<String, String> pair : Utilities.StateAbbreviationsAndNames.entrySet())
				if(pair.getValue().equals(stateName))
					return pair.getKey();
		}
		return stateName;
	}
	
	public static String getStateName(String stateAbbreviation) {
		if(stateAbbreviation.length() != 2)
			return stateAbbreviation;
		if(Utilities.StateAbbreviationsAndNames.containsKey(stateAbbreviation))
			return Utilities.StateAbbreviationsAndNames.get(stateAbbreviation);
		return stateAbbreviation;
	}
}
